from
howtographql.com
API需求:
Retrieve a list(feed) of link elements- Allow users to
signup upwith their name, email and password - Users who signed up should be able to
loginagain with their email and password - Allow authenticated users to
postnew link elements - Allow authenticated users to
upvotean existing link element - Send
realtime updatesto subscribed clients when a new link element is created - Send
realtime updatesto subscribed clients when an existing link element is upvoted
定义 应用 schema
这一步非常好,根据应用的需求首先定义需要实现的 query,mutation,subscribe,然后围绕这个schema 展开工作,目前我们不关注User,Link,Vote的实现细节
1 | type Query { |
接下来就是逐渐实现这些操作.每一步实现的流程实际是基本类似的-这也就是schema-driven-development
- 调整 data model
- 调整之后,部署服务
- 用新的根字段扩展应用的 schema
- 实现 resolver, 通过代理执行对应的 Prisma resolver
初始化文件结构
1 | . |
src包含应用的实现代码和应用schema(application schema),Prisma根据 dataModel生成的Prisma schemadatabase包含初始化配置文件 prisma.yml和应用的 data model
src/schema.graphql(Apllication schema)定义了暴露给 client 端的 graphql API.
src/generated/prisma.graphql(Prisma schema)定义了对数据库操作的 CRUD API. 对于在 data model 中的每个类型, Prisma会生成读写数据库节点的操作
data model并不是实际的 Graphql schema,缺少 root type. data model 用于生成实际执行的 schema.
理解初始化步骤
有两个依赖包:
graphql-yoga: GraphQL Server 需要的文件,实际是 express 服务器prisma-binding: 可以允许绑定应用 schema 的 resolvers到自动生成的 Prisma database服务.
1 | const server = new GraphQLServer({ |
查询
在 data model 中定义 Link type
1 | type Link { |
yarn prisma deploy 之后
会自动生成prisma.graphql
1 | type Query { |
对 应用的 schema 做出调整
定义feed查询
1 | # import Link from "./generated/prisma.graphql" |
实现 feed的 resolver
每个Query,Mutation都由 resolver 支撑. 相应的定义为 Query.js,Mutation.js,Subscription.js.
1 | function feed(parent, args, context, info) { |
resolver 函数接收四个参数,
parent:包含 resolver 链的初始值args:包含查询参数的对象.context:包含定制的数据,可以在 resolver 链中传递.例如每个 resolver 都可以读写info包含了 AST信息
打开 index.js替换下面的resolver对象:
1 | const resolvers = { |
测试 API
在项目的根目录执行
1 | yarn dev |
打开 graphiQL, 输入
1 | mutation { |
Mutations
Mutation for posting new links
打开 src/schema.graphql添加如下代码
1 | type Mutation { |
在src/resolvers创建新的Mutation.js,添加代码:
1 | function post(parent, args, context, info) { |
在index.js中添加新的mutation声明
1 | const Mutation = require('./resolvers/Mutation') |
测试一下
1 | mutation { |
Signup&Login
为用户提供登录功能
Signup
注册的实现步骤:
- 服务器收到新用户的
email,password(还有name) - 服务器在数据库创建新用户,并储存
name,email和 hash过的密码 - 服务器创建一个认证 token(JWT)
- 服务器把 token返回给用户
Login
- 服务器收到
loginmutation - 服务器比较
password的一致性 - 如果密码一致, 服务器创建一个 token
- 服务器把 token 返回给用户
实现注册流程
创建secret
1 | const APP_SECRET = 'GraphQL-is-aw3some' |
在应用的 schema 中添加 signup Mutation
1 | type Mutation { |
src/schema.graphql
1 | type AuthPayload { |
database/datamodel.graphql
1 | type User { |
部署以后会生成
1 | type Query { |
打开应用 schema src/schema.graphql添加如下的类型
1 | type User{ |
这里的定义和 Prisma schema 里的定义类似,但是没有password,也就是不允许用户查询 password
接下来需要signup mutation
1 | async function signup(parent, args, context, info) { |
在signup resolver 中, 首先创建密码的加密版本, 接下来, 使用Prisma的实例,创建新的User节点.最后返回包含AuthPayload包含创建的 token 和新的 user 对象
实现login mutation
接下来,实现login mutation.
src/schema.graphql中添加Mutation 类型
1 | type Mutation { |
打开 src/resolvers/Mutation.js添加如下函数
1 | async function login(parent, args, context, info) { |
首先使用 email 查询是否存在, 然后比较密码是否一致. 完成后,返回一个 token 给用户
应用 Authentication
创建User和Link的关联
在需求中,我们提出只有通过认证的用户才可以创建新的 Link元素.
打开 database/datamodel.js 改变User和Link类型
1 | type Link { |
可以执行如下的操作
1 | mutation { |
打开 src/resolvers/Mutation.js改变post
1 | function post(parent, { url, description }, context, info) { |
和之前的版本不同的地方是,首先获取到用户的 id, 然后传递给createLink-mutation 作为connect的参数.
现在我们需要一个工具函数 getUserId,
src/utils.js
1 | function getUserId(context) { |
context参数有一个request属性,代表着携带 query 或者 mutation的 HTTP 请求. 重要的是,它提供了访问头 .token就在头部中
认证一个用户
- 在 playground中, 发送
signup或者loginmutation,从 graphql server 中获取到token. - 在
PlayGround中设置Header - 发送
postmutation,创建新的Link元素
1 | mutation { |
设置好以后,执行
1 | mutation { |
投票部分
Subscriptions
实现 GraphQL订阅
打开src/schema.graphql,添加Subscription
1 | type Subscription { |
在src/resolvers创建一个新的文件, SubScription.js添加如下代码:
1 | const newLink = { |
现在的src/inde.js文件
1 | const { GraphQLServer } = require('graphql-yoga') |